use serde::{Deserialize, Serialize};
use std::fmt;

const DEBUG: bool = false;
pub enum MsgFlag {
    RECORDING,
    DOWNLOAD,
    UPLOAD,
    DEVICE, //设备, 摄像头和麦克风等
}

impl fmt::Display for MsgFlag {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        let s = match self {
            MsgFlag::RECORDING => "RECORDING",
            MsgFlag::DOWNLOAD => "DOWNLOAD",
            MsgFlag::UPLOAD => "UPLOAD",
            MsgFlag::DEVICE => "DEVICE",
        };
        write!(f, "{}", s)
    }
}

pub enum MsgStatus {
    START,
    PROGRESS,
    SUCCESS,
    ERROR,
}

impl fmt::Display for MsgStatus {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        let s = match self {
            MsgStatus::START => "START",
            MsgStatus::PROGRESS => "PROGRESS",
            MsgStatus::SUCCESS => "SUCCESS",
            MsgStatus::ERROR => "ERROR",
        };
        write!(f, "{}", s)
    }
}

#[derive(Clone, Serialize, Deserialize, Debug, Default)]
pub struct Message {
    pub id: u32,
    pub flag: String,
    pub status: String,
    pub key: String,
    pub content: String,
}
impl Message {
    pub fn new(flag: MsgFlag, status: MsgStatus, key: String, content: String) -> Self {
        Message {
            id: 0,
            flag: flag.to_string(),
            status: status.to_string(),
            key,
            content,
        }
    }
}

const DB_NAME: &str = "logs/bwexam.db";
/// 消息管理器
pub struct MessageManager {
    db_name: String,
}

impl MessageManager {
    pub fn new() -> Self {
        MessageManager {
            db_name: DB_NAME.to_string(),
        }
    }

    pub fn init(&mut self, exec_init_sql: bool) {
        let conn = self.get_conn(false).unwrap();
        if exec_init_sql {
            // 创建 message 表
            conn.execute_batch(
                " DROP TABLE IF EXISTS message;
        CREATE TABLE IF NOT EXISTS message (
            id INTEGER PRIMARY KEY AUTOINCREMENT,
            flag TEXT NOT NULL,
            status TEXT NOT NULL,
            key  TEXT NOT NULL,
            content TEXT,
            UNIQUE(key, status)
        );
        ",
            )
            .unwrap();
        }
    }

    fn execute_sql(&self, sql: impl AsRef<str>, checkpoint: bool) -> Result<(), String> {
        if DEBUG {
            log::info!("SQL execute: {}", sql.as_ref());
        }
        let conn = self
            .get_conn(false)
            .map_err(|err| format!("Failed to get DB connection: {}", err))?;
        conn.execute_batch(sql.as_ref())
            .map_err(|err| format!("SQL execution error: {}", err))?;
        if checkpoint {
            // PASSIVE
            conn.pragma_update(None, "wal_checkpoint", &"PASSIVE")
                .map_err(|err| format!("wal_checkpoint error: {}", err))?;
        }
        Ok(())
    }

    fn get_conn(&self, readonly: bool) -> Result<rusqlite::Connection, String> {
        // 创建 SQLite 连接（可替换为文件数据库）
        // let conn = Connection::open_in_memory();
        let flag = match readonly {
            true => rusqlite::OpenFlags::SQLITE_OPEN_READ_ONLY,
            false => rusqlite::OpenFlags::default(),
        };
        let mut r =
            rusqlite::Connection::open_with_flags(self.db_name.as_str(), flag).map_err(|err| {
                log::error!("open db error:{:?}", err);
                "无法打开本地数据库".to_string()
            });
        // https://www.cnblogs.com/cchust/p/4754619.html
        // SQLite中 日志模式 主要有DELETE和WAL两种
        // WAL（Write-Ahead Logging）模式是SQLite中一种可选的日志模式，可以提供更好的并发性能。
        // WAL模式下，数据文件何时更新呢？这里引入了检查点概念，检查点的作用就是定期将日志中的新页覆盖DB文件中的老页，并通过参数wal_autocheckpoint来控制检查点时机，达到权衡读写的目的。
        // 在WAL模式下，每个进程都有自己的日志文件，不再需要对数据库文件加锁。这意味着多个进程可以同时读写数据库，提高并发性能。同时，WAL模式也支持回滚操作，可以回滚未提交的事务。
        // 除了WAL模式，SQLite还提供了基于文件锁的并发控制机制。通过在打开数据库时设置适当的锁类型，我们可以控制进程之间的并发访问。
        // 通过 PRAGMA schema.wal_checkpoint 命令可以手工触发一次检查点，比如PRAGMA schema.wal_checkpoint(PASSIVE)触发一次PASSIVE类型检查点，PRAGMA schema.wal_checkpoint(FULL)触发一次FULL类型检查点。对于FULL类型，由于需要将所有更新合并到DB文件，如果有读写事务没有结束，则需要等待；而且做检查点过程中，会堵塞新的读写事务。所以PASSIVE类型不会导致锁等待，而FULL类型，RESTART和TRUNCATE类型，会导致锁等待。
        // PASSIVE，默认自动检查点和主动检查点都是PASSIVE类型，将所有可以同步到db的数据都进行同步(不超过所有线程的end mark)，不持有排它锁，因此不会影响其他读写事务。
        // FULL,将wal与db文件完全同步，需要等待所有读写事务都结束，并且会堵塞新的读写事务
        // 使用文件锁，我们可以确保同一时间只有一个进程可以打开和写入数据库文件。
        // journal_mode(日志模式)
        // 默认是DELETE模式
        // DELETE：原始数据页存放在日志文件中，事务提交时，将文件删除。
        // TRUNCATE ：与DELETE模式的区别是，清空日志文件，但不删除文件清空文件往往比删除文件要快。
        // PERSIST：与DELETE和TRUNCATE模式区别是，既不删除文件，也不清空文件，而是将日志文件第一个页设置标记(置0)，这个也是为了提高性能。
        // MEMORY ：内存模式，修改不落盘，无法保证事务的原子性。
        // OFF：不开启日志，这样没法保证事务的原子性。
        // WAL ：write ahead log，3.7.0引入，日志中记录修改页，提交时只需刷修改页 通过命令pragma journal_mode=wal可以开启wal模式
        if let Ok(conn) = r.as_mut() {
            conn.pragma_update(None, "journal_mode", &"WAL").unwrap();
            conn.busy_timeout(std::time::Duration::from_secs(1))
                .unwrap();
        }
        r
    }

    fn query(&self, sql: impl AsRef<str>) -> Result<Vec<Message>, String> {
        if DEBUG {
            log::info!("SQL query: {}", sql.as_ref());
        }
        let conn = self.get_conn(true).unwrap();
        let mut stmt = conn.prepare(sql.as_ref()).unwrap();
        let message_iter = stmt
            .query_map([], |row| {
                Ok(Message {
                    id: row.get(0).unwrap(),
                    flag: row.get(1).unwrap(),
                    status: row.get(2).unwrap(),
                    key: row.get(3).unwrap(),
                    content: row.get(4).unwrap(),
                })
            })
            .unwrap();

        let mut messages = Vec::new();
        for msg_result in message_iter {
            messages.push(msg_result.unwrap());
        }
        if DEBUG {
            log::info!("dbmessages: {:?}", &messages);
        }
        Ok(messages)
    }

    pub fn delete_device(&self) -> Result<(), String> {
        self.execute_sql(
            format!("DELETE FROM message WHERE flag='{}'", MsgFlag::DEVICE),
            false,
        )
    }
    pub fn insert_device(&self, key: &str, content: &str) -> Result<(), String> {
        self.execute_sql(
            format!(
            "INSERT INTO message (flag, status, key, content) VALUES ('{}', 'ERROR', '{}', '{}')",
            MsgFlag::DEVICE,
            key,
            content,
        ),
            false,
        )
    }
    pub fn update_device(&self, id: u32) -> Result<(), String> {
        self.execute_sql(
            format!("UPDATE  message SET status='SUCCESS' WHERE id = {}", id),
            false,
        )
    }

    pub fn get_device(&self) -> Result<Vec<Message>, String> {
        self.query("SELECT * FROM message where flag='DEVICE' ORDER BY id asc")
    }

    pub fn get_audio_video(&self) -> (Option<String>, Option<String>) {
        let mut audio: Option<String> = None;
        let mut video: Option<String> = None;
        let list = self.query(format!(
            "SELECT * FROM message where flag='{}'  and status='SUCCESS'",
            MsgFlag::DEVICE
        ));
        if let Ok(messages) = list {
            for msg in messages {
                if msg.key.starts_with("AUDIO") {
                    audio.replace(msg.content);
                } else if msg.key.starts_with("VIDEO") {
                    video.replace(msg.content);
                }
            }
        }
        (audio, video)
    }

    pub fn get_message(&self) -> Result<Vec<Message>, String> {
        self.query("SELECT * FROM message where flag<>'DEVICE' ORDER BY id asc")
    }

    pub fn send_delete(&self, id: u32) -> Result<(), String> {
        self.execute_sql(format!("DELETE FROM message WHERE id = {}", id), false)
    }

    pub fn send_msg(&self, message: Message) -> Result<(), String> {
        let flag = message.flag;
        let status = message.status;
        let key = message.key;
        let content = message.content;
        let sql = match status.as_str() {
            "PROGRESS" => {
                //  1.58 开始支持,只会保留一条 PROGRESS 记录, 避免重复插入
                format!(
                "INSERT INTO message (flag, status, key, content) VALUES ('{flag}', '{status}', '{key}', '{content}') \
                 ON CONFLICT(key, status) DO UPDATE SET content = '{content}';"
            )
            }
            "SUCCESS" | "ERROR" => {
                format!(
                "DELETE FROM message where key = '{key}' and  flag = '{flag}' and status='PROGRESS' ; \
                 INSERT INTO message (flag, status, key, content) VALUES ('{flag}', '{status}', '{key}', '{content}'); "
            )
            }
            _ => {
                format!("INSERT INTO message (flag, status, key, content) VALUES ('{flag}', '{status}', '{key}', '{content}') ;")
            }
        };

        self.execute_sql(sql, false)
    }
}
